﻿using LittleBee.IDAL;
using LittleBee.Utils;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Text;

namespace LittleBee.Cells
{
	public abstract class Cell : EventLoop
	{
		/// <summary>
		/// the key list of Pointer
		/// </summary>
		public const string ATTRIBUTES = "attributes";
		public const string POINTERS = "pointers";
		public const string PTRS = "ptrs";

		public const string APPID = "appid";


		//private string _strCellType;
		protected Dictionary<string, Pointer> _dctProperties = new Dictionary<string, Pointer>();

		public Int64 InstanceID { get => UUID.ObjectID; }

		/// <summary>
		/// get pointer through pid
		/// </summary>
		/// <param name="pid"></param>
		/// <returns></returns>
		public Pointer this[string pid]
		{
			get
			{
				return _dctProperties[pid];
			}
		}

		/// <summary>
		/// get pointer through array index
		/// </summary>
		/// <param name="pid"></param>
		/// <param name="idx"></param>
		/// <returns></returns>
		public Pointer this[string pid, int idx]
		{
			get
			{
				return _dctProperties[_dctProperties[pid][idx]];
			}
		}

		/// <summary>
		/// get pointer through struct field name
		/// </summary>
		/// <param name="pid"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		public Pointer this[string pid, string key]
		{
			get
			{
				return _dctProperties[_dctProperties[pid][key]];
			}
		}

		/// <summary>
		/// get pointer through struct field name of array
		/// </summary>
		/// <param name="pid"></param>
		/// <param name="idx"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		public Pointer this[string pid, int idx, string key]
		{
			get
			{
				return this[_dctProperties[pid][idx], key];
			}
		}

		//public Cell()
		//	: base() { OnConstruct(); }

		public Cell(Int64 iid)
			: base(iid)
		{
		}

		protected override bool IsFirstLoaded
		{
			get => CellService.Instance.IsFirstLoaded(InstanceID);
			set { OnFirstLoad(); CellService.Instance.RemoveFirstLoaded(InstanceID); }
		}

		/// <summary>
		/// Cell's Monitoring State: true: any changing of cell and pointer of cell will be notified to call CellService.PointerUpdated event
		/// </summary>
		internal bool MonitoringState
		{
			get; set;
		} = false;


		public Int64 AppID { get => UUID.ObjectID; }
		public Int64 CellID { get; set; }

		public sealed override void Initialize()
		{
			base.Initialize();
		}

		public sealed override void finalize()
		{
			base.finalize();
		}

		protected sealed override void Construct()
		{
			LoadAppPointers();
		}

		private void LoadAppPointers()
		{
			CellService.Instance.QueryAppPointers(InstanceID, out DataTable? dtPointers);

			foreach (DataRow item in dtPointers!.Rows)
			{
				string pid = item.Field<string>("pid")!;
				string name = item.Field<string>("name")!;
				string desc = item.Field<string>("description")!;
				Enum.TryParse<Pointer.Attributes>(item.Field<long>("attribute").ToString(), out Pointer.Attributes attr);
				string pvt = item.Field<string>("type")!;
				JObject joVal = JObject.Parse(item.Field<string>("value")!);
				JObject joHelper = JObject.Parse(item.Field<string>("helper")!);

				Pointer p = Pointer.Create(pid, name, desc, attr, pvt, joVal, joHelper);
				p.PointerPropertyChanged += OnPointerPropertyChanged;
				p.PointerPropertyChanging += OnPointerPropertyChanging;

				_dctProperties[p.Pid] = p;
			}
		}

		public override void Dispose()
		{
			throw new NotImplementedException();
		}

		public virtual bool SetPointerValue(JObject joPointer, string pid)
		{
			bool blRet = true;

			try
			{
				this[pid].SetValue(joPointer);
			}
			catch (Exception ex)
			{
				Console.WriteLine($"Exception occurred: {ex.Message}\n\n{ex.StackTrace}");
				blRet = false;
			}
			return blRet;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="joPointers">[{pid:pid, val:val}, ...]</param>
		/// <param name="jtResp">["appid:pid", ...]</param>
		/// <param name="blBreak">
		///		true:	break when error occurred
		///		false:	continue when error occurred
		/// </param>
		/// <returns></returns>
		public virtual bool SetPointersValue(JArray jaPointers, JArray jaResp, bool blBreak = true)
		{
			bool blRet = true;

			lock (_dctProperties)
			{
				if (OnCheckPointersValue(jaPointers) == true)
				{
					foreach (JObject joPointer in jaPointers)
					{
						string pid = joPointer.Value<string>(Pointer.PID)!;
						if (SetPointerValue(pid, joPointer) == false)
						{
							blRet = false;
							jaResp.Add($"{AppID}:{pid}");

							if (blBreak == true)
								break;
						}
					}
				}
				else
				{
					blRet = false;
				}
			}

			return blRet;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="pid"></param>
		/// <param name="joValue"></param>
		/// <returns></returns>
		protected virtual bool SetPointerValue(string pid, JObject joValue)
		{
			bool blRet = false;
			try
			{
				_dctProperties[pid].SetValue(joValue);

				blRet = true;
			}
			catch(Exception ex)
			{
				Console.WriteLine($"Exception: {ex.Message}\n{ex.StackTrace}");
			}

			return blRet;
		}

		//private bool SetPointerValue(string pid, JToken jtValue)
		//{
		//	return SetPointerValue(pid, Pointer.VAL, jtValue);
		//}

		//private bool SetPointerValue(string pid, string key, JToken value)
		//{
		//	bool blRet = false;

		//	JObject joValue = new JObject
		//	{
		//		[key] = value
		//	};

		//	blRet = SetPointerValue(pid, joValue);

		//	return blRet;
		//}

		public override void OnInitialize()
		{
			//throw new NotImplementedException();
		}

		public override void OnConstruct()
		{
			//throw new NotImplementedException();
		}

		public override void OnRunLoop()
		{
			//throw new NotImplementedException();
		}
		public override void OnFirstLoad()
		{
			//throw new NotImplementedException();
		}
		public override void OnStop()
		{
			//throw new NotImplementedException();
		}
		public override void OnUninitialize()
		{
			//throw new NotImplementedException();
		}
		public override void OnRestart()
		{
			//throw new NotImplementedException();
		}
		public override void OnPause()
		{
			//throw new NotImplementedException();
		}

		/// <summary>
		/// 在SetPointersValue时，可以在设定Pointer值之前对pointers队列的每一个值进行一次调整，或者取消这次设定动作
		/// </summary>
		/// <param name="joPointers">预设定的pointer值队列</param>
		/// <returns>
		///		true： 继续SetPointersValue
		///		false：取消这次设定动作
		/// </returns>
		protected override bool OnCheckPointersValue(JArray joPointers)
		{
			return true;
		}

		protected virtual bool OnPointerPropertyChanging(object sender, PointerPropertyChangingEventArgs args)
		{
			return true;
		}

		protected virtual void OnPointerPropertyChanged(object sender, PointerPropertyChangedEventArgs args)
		{
			Pointer p = (sender as Pointer)!;

			CellService.Instance.UpdateValueChangedPointer(this, p, args.Property);

			Console.WriteLine($"{p.Name} : {args.Property} = {p}");
		}

		private static string strAttributes =
			@"[{
				pid:'CellName',
				name:'Cell Name',
				description:'The Cell Name',
				attribute:0x040,
				type:'string',
				value:{},
				helper:{}
			},
			{
				pid:'CellType',
				name:'Cell Type',
				description:'The Cell Type',
				attribute:0x0C0,
				type:'string',
				value:{},
				helper:{}
			}]";
		public static void GetCellSharedPointers(out JArray jaAttrs)
		{
			jaAttrs = JArray.Parse(strAttributes);
		}
	}
}
